/*
 * May 29, 2005
 */
package traer.physics;

import java.util.*;

public class ParticleSystem {

    public static final int RUNGE_KUTTA = 0;
    public static final int MODIFIED_EULER = 1;
    protected static final float DEFAULT_GRAVITY = 0;
    protected static final float DEFAULT_DRAG = 0.001f;
    ArrayList particles;
    ArrayList springs;
    ArrayList attractions;
    ArrayList customForces = new ArrayList();
    Integrator integrator;
    Vector3D gravity;
    float drag;
    boolean hasDeadParticles = false;

    public final void setIntegrator(int integrator) {
        switch (integrator) {
            case RUNGE_KUTTA:
                this.integrator = new RungeKuttaIntegrator(this);
                break;
            case MODIFIED_EULER:
                this.integrator = new ModifiedEulerIntegrator(this);
                break;
        }
    }

    public final void setGravity(float x, float y, float z) {
        gravity.set(x, y, z);
    }

    // default down gravity
    public final void setGravity(float g) {
        gravity.set(0, g, 0);
    }

    public final void setDrag(float d) {
        drag = d;
    }

    public final void tick() {
        tick(1);
    }

    public final void tick(float t) {
        integrator.step(t);
    }

    public final Particle makeParticle(float mass, float x, float y, float z) {
        Particle p = new Particle(mass);
        p.position().set(x, y, z);
        particles.add(p);
        return p;
    }

    public final Particle makeParticle() {
        return makeParticle(1.0f, 0f, 0f, 0f);
    }

    public final Spring makeSpring(Particle a, Particle b, float ks, float d, float r) {
        Spring s = new Spring(a, b, ks, d, r);
        springs.add(s);
        return s;
    }

    public final Attraction makeAttraction(Particle a, Particle b, float k, float minDistance) {
        Attraction m = new Attraction(a, b, k, minDistance);
        attractions.add(m);
        return m;
    }

    public final void clear() {
        particles.clear();
        springs.clear();
        attractions.clear();
    }

    public ParticleSystem(float g, float somedrag) {
        integrator = new RungeKuttaIntegrator(this);
        particles = new ArrayList();
        springs = new ArrayList();
        attractions = new ArrayList();
        gravity = new Vector3D(0, g, 0);
        drag = somedrag;
    }

    public ParticleSystem(float gx, float gy, float gz, float somedrag) {
        integrator = new RungeKuttaIntegrator(this);
        particles = new ArrayList();
        springs = new ArrayList();
        attractions = new ArrayList();
        gravity = new Vector3D(gx, gy, gz);
        drag = somedrag;
    }

    public ParticleSystem() {
        integrator = new RungeKuttaIntegrator(this);
        particles = new ArrayList();
        springs = new ArrayList();
        attractions = new ArrayList();
        gravity = new Vector3D(0, ParticleSystem.DEFAULT_GRAVITY, 0);
        drag = ParticleSystem.DEFAULT_DRAG;
    }

    protected final void applyForces() {
        if (!gravity.isZero()) {
            for (int i = 0; i < particles.size(); ++i) {
                Particle p = (Particle) particles.get(i);
                p.force.add(gravity);
            }
        }

        for (int i = 0; i < particles.size(); ++i) {
            Particle p = (Particle) particles.get(i);
            p.force.add(p.velocity.x() * -drag, p.velocity.y() * -drag, p.velocity.z() * -drag);
        }

        for (int i = 0; i < springs.size(); i++) {
            Spring f = (Spring) springs.get(i);
            f.apply();
        }

        for (int i = 0; i < attractions.size(); i++) {
            Attraction f = (Attraction) attractions.get(i);
            f.apply();
        }

        for (int i = 0; i < customForces.size(); i++) {
            Force f = (Force) customForces.get(i);
            f.apply();
        }
    }

    protected final void clearForces() {
        Iterator i = particles.iterator();
        while (i.hasNext()) {
            Particle p = (Particle) i.next();
            p.force.clear();
        }
    }

    public final int numberOfParticles() {
        return particles.size();
    }

    public final int numberOfSprings() {
        return springs.size();
    }

    public final int numberOfAttractions() {
        return attractions.size();
    }

    public final Particle getParticle(int i) {
        return (Particle) particles.get(i);
    }

    public final Spring getSpring(int i) {
        return (Spring) springs.get(i);
    }

    public final Attraction getAttraction(int i) {
        return (Attraction) attractions.get(i);
    }

    public final void addCustomForce(Force f) {
        customForces.add(f);
    }

    public final int numberOfCustomForces() {
        return customForces.size();
    }

    public final Force getCustomForce(int i) {
        return (Force) customForces.get(i);
    }

    public final Force removeCustomForce(int i) {
        return (Force) customForces.remove(i);
    }

    public final void removeParticle(Particle p) {
        particles.remove(p);
    }

    public final Spring removeSpring(int i) {
        return (Spring) springs.remove(i);
    }

    public final Attraction removeAttraction(int i) {
        return (Attraction) attractions.remove(i);
    }

    public final void removeAttraction(Attraction s) {
        attractions.remove(s);
    }

    public final void removeSpring(Spring a) {
        springs.remove(a);
    }

    public final void removeCustomForce(Force f) {
        customForces.remove(f);
    }
}
